home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CUCD / Utilities / vim-5.1 / src / window.c < prev   
Encoding:
C/C++ Source or Header  |  1998-03-29  |  47.4 KB  |  2,113 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved    by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read a list of people who contributed.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. #include "vim.h"
  10.  
  11. #ifdef FILE_IN_PATH
  12. static char_u *find_file_in_path __ARGS((char_u *ptr, int len, int options, long count));
  13. static char_u *find_file_in_wildcard_path __ARGS((char_u *path_so_far, char_u *wildcards, int level, long *countptr));
  14. static int path_is_url __ARGS((char_u *p));
  15. #endif
  16. static void reset_VIsual __ARGS((void));
  17. static int win_comp_pos __ARGS((void));
  18. static void win_exchange __ARGS((long));
  19. static void win_rotate __ARGS((int, int));
  20. static void win_goto __ARGS((WIN *wp));
  21. static void win_append __ARGS((WIN *, WIN *));
  22. static void win_remove __ARGS((WIN *));
  23. static void win_new_height __ARGS((WIN *, int));
  24.  
  25. static WIN    *prevwin = NULL;    /* previous window */
  26.  
  27. #define URL_SLASH    1        /* path_is_url() has found "://" */
  28. #define URL_BACKSLASH    2        /* path_is_url() has found ":\\" */
  29.  
  30. /*
  31.  * all CTRL-W window commands are handled here, called from normal_cmd().
  32.  */
  33.     void
  34. do_window(nchar, Prenum)
  35.     int        nchar;
  36.     long    Prenum;
  37. {
  38.     long    Prenum1;
  39.     WIN        *wp;
  40.     int        xchar;
  41. #if defined(FILE_IN_PATH) || defined(FIND_IN_PATH)
  42.     char_u  *ptr;
  43. #endif
  44. #ifdef FIND_IN_PATH
  45.     int        type = FIND_DEFINE;
  46.     int        len;
  47. #endif
  48.  
  49.     if (Prenum == 0)
  50.     Prenum1 = 1;
  51.     else
  52.     Prenum1 = Prenum;
  53.  
  54.     switch (nchar)
  55.     {
  56. /* split current window in two parts */
  57.     case 'S':
  58.     case Ctrl('S'):
  59.     case 's':    reset_VIsual();            /* stop Visual mode */
  60.         win_split((int)Prenum, TRUE, FALSE);
  61.         break;
  62.  
  63. /* split current window and edit alternate file */
  64.     case K_CCIRCM:
  65.     case '^':
  66.         reset_VIsual();            /* stop Visual mode */
  67.         stuffReadbuff((char_u *)":split #");
  68.         if (Prenum)
  69.             stuffnumReadbuff(Prenum);    /* buffer number */
  70.         stuffcharReadbuff('\n');
  71.         break;
  72.  
  73. /* open new window */
  74.     case Ctrl('N'):
  75.     case 'n':    reset_VIsual();            /* stop Visual mode */
  76.         stuffcharReadbuff(':');
  77.         if (Prenum)
  78.             stuffnumReadbuff(Prenum);        /* window height */
  79.         stuffReadbuff((char_u *)"new\n");   /* it is ex_docmd.c */
  80.         break;
  81.  
  82. /* quit current window */
  83.     case Ctrl('Q'):
  84.     case 'q':    reset_VIsual();            /* stop Visual mode */
  85.         stuffReadbuff((char_u *)":quit\n"); /* it is ex_docmd.c */
  86.         break;
  87.  
  88. /* close current window */
  89.     case Ctrl('C'):
  90.     case 'c':    reset_VIsual();            /* stop Visual mode */
  91.         stuffReadbuff((char_u *)":close\n");    /* it is ex_docmd.c */
  92.         break;
  93.  
  94. /* close all but current window */
  95.     case Ctrl('O'):
  96.     case 'o':    reset_VIsual();            /* stop Visual mode */
  97.         stuffReadbuff((char_u *)":only\n"); /* it is ex_docmd.c */
  98.         break;
  99.  
  100. /* cursor to next window */
  101.     case 'j':
  102.     case K_DOWN:
  103.     case Ctrl('J'):
  104.         for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0;
  105.                                 wp = wp->w_next)
  106.             ;
  107.         win_goto(wp);
  108.         break;
  109.  
  110. /* cursor to next window with wrap around */
  111.     case Ctrl('W'):
  112.     case 'w':
  113. /* cursor to previous window with wrap around */
  114.     case 'W':
  115.         if (lastwin == firstwin)    /* just one window */
  116.             beep_flush();
  117.         else
  118.         {
  119.             if (Prenum)            /* go to specified window */
  120.             {
  121.             for (wp = firstwin; --Prenum > 0; )
  122.             {
  123.                 if (wp->w_next == NULL)
  124.                 break;
  125.                 else
  126.                 wp = wp->w_next;
  127.             }
  128.             }
  129.             else
  130.             {
  131.             if (nchar == 'W')        /* go to previous window */
  132.             {
  133.                 wp = curwin->w_prev;
  134.                 if (wp == NULL)
  135.                 wp = lastwin;        /* wrap around */
  136.             }
  137.             else                /* go to next window */
  138.             {
  139.                 wp = curwin->w_next;
  140.                 if (wp == NULL)
  141.                 wp = firstwin;        /* wrap around */
  142.             }
  143.             }
  144.             win_goto(wp);
  145.         }
  146.         break;
  147.  
  148. /* cursor to window above */
  149.     case 'k':
  150.     case K_UP:
  151.     case Ctrl('K'):
  152.         for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0;
  153.                                 wp = wp->w_prev)
  154.             ;
  155.         win_goto(wp);
  156.         break;
  157.  
  158. /* cursor to top window */
  159.     case 't':
  160.     case Ctrl('T'):
  161.         wp = firstwin;
  162.         win_goto(wp);
  163.         break;
  164.  
  165. /* cursor to bottom window */
  166.     case 'b':
  167.     case Ctrl('B'):
  168.         wp = lastwin;
  169.         win_goto(wp);
  170.         break;
  171.  
  172. /* cursor to last accessed (previous) window */
  173.     case 'p':
  174.     case Ctrl('P'):
  175.         if (prevwin == NULL)
  176.             beep_flush();
  177.         else
  178.         {
  179.             wp = prevwin;
  180.             win_goto(wp);
  181.         }
  182.         break;
  183.  
  184. /* exchange current and next window */
  185.     case 'x':
  186.     case Ctrl('X'):
  187.         win_exchange(Prenum);
  188.         break;
  189.  
  190. /* rotate windows downwards */
  191.     case Ctrl('R'):
  192.     case 'r':    reset_VIsual();            /* stop Visual mode */
  193.         win_rotate(FALSE, (int)Prenum1);    /* downwards */
  194.         break;
  195.  
  196. /* rotate windows upwards */
  197.     case 'R':    reset_VIsual();            /* stop Visual mode */
  198.         win_rotate(TRUE, (int)Prenum1);        /* upwards */
  199.         break;
  200.  
  201. /* make all windows the same height */
  202.     case '=':    win_equal(NULL, TRUE);
  203.         break;
  204.  
  205. /* increase current window height */
  206.     case '+':    win_setheight(curwin->w_height + (int)Prenum1);
  207.         break;
  208.  
  209. /* decrease current window height */
  210.     case '-':    win_setheight(curwin->w_height - (int)Prenum1);
  211.         break;
  212.  
  213. /* set current window height */
  214.     case Ctrl('_'):
  215.     case '_':    win_setheight(Prenum ? (int)Prenum : 9999);
  216.         break;
  217.  
  218. /* jump to tag and split window if tag exists */
  219.     case ']':
  220.     case Ctrl(']'):
  221.         reset_VIsual();            /* stop Visual mode */
  222.         if (Prenum)
  223.             postponed_split = Prenum;
  224.         else
  225.             postponed_split = -1;
  226.         stuffcharReadbuff(Ctrl(']'));
  227.         break;
  228.  
  229. #ifdef FILE_IN_PATH
  230. /* edit file name under cursor in a new window */
  231.     case 'f':
  232.     case Ctrl('F'):
  233.         reset_VIsual();            /* stop Visual mode */
  234.         ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP,
  235.                                      Prenum1);
  236.         if (ptr != NULL)
  237.         {
  238.             setpcmark();
  239.             if (win_split(0, FALSE, FALSE) == OK)
  240.             (void)do_ecmd(0, ptr, NULL, NULL, (linenr_t)0,
  241.                                    ECMD_HIDE);
  242.             vim_free(ptr);
  243.         }
  244.         break;
  245. #endif
  246.  
  247. #ifdef FIND_IN_PATH
  248. /* Go to the first occurence of the identifier under cursor along path in a
  249.  * new window -- webb
  250.  */
  251.     case 'i':                /* Go to any match */
  252.     case Ctrl('I'):
  253.         type = FIND_ANY;
  254.         /* FALLTHROUGH */
  255.     case 'd':                /* Go to definition, using p_def */
  256.     case Ctrl('D'):
  257.         if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
  258.             break;
  259.         find_pattern_in_path(ptr, 0, len, TRUE,
  260.             Prenum == 0 ? TRUE : FALSE, type,
  261.             Prenum1, ACTION_SPLIT, (linenr_t)1, (linenr_t)MAXLNUM);
  262.         curwin->w_set_curswant = TRUE;
  263.         break;
  264. #endif
  265.  
  266. /* CTRL-W g  extended commands */
  267.     case 'g':
  268. #ifdef USE_GUI_WIN32
  269.         dont_scroll = TRUE;        /* disallow scrolling here */
  270. #endif
  271.         ++no_mapping;
  272.         ++allow_keys;   /* no mapping for xchar, but allow key codes */
  273.         xchar = vgetc();
  274. #ifdef HAVE_LANGMAP
  275.         LANGMAP_ADJUST(xchar, TRUE);
  276. #endif
  277.         --no_mapping;
  278.         --allow_keys;
  279. #ifdef SHOWCMD
  280.         (void)add_to_showcmd(xchar);
  281. #endif
  282.         switch (xchar)
  283.         {
  284.             case ']':
  285.             case Ctrl(']'):
  286.             reset_VIsual();            /* stop Visual mode */
  287.             if (Prenum)
  288.                 postponed_split = Prenum;
  289.             else
  290.                 postponed_split = -1;
  291.             stuffcharReadbuff('g');
  292.             stuffcharReadbuff(xchar);
  293.             break;
  294.  
  295.             default:
  296.             beep_flush();
  297.             break;
  298.         }
  299.         break;
  300.  
  301.     default:    beep_flush();
  302.         break;
  303.     }
  304. }
  305.  
  306.     static void
  307. reset_VIsual()
  308. {
  309.     if (VIsual_active)
  310.     {
  311.     end_visual_mode();
  312.     update_curbuf(NOT_VALID);    /* delete the inversion */
  313.     }
  314. }
  315.  
  316. /*
  317.  * split the current window, implements CTRL-W s and :split
  318.  *
  319.  * new_height is the height for the new window, 0 to make half of current
  320.  * height
  321.  * redraw is TRUE when redraw now
  322.  *
  323.  * return FAIL for failure, OK otherwise
  324.  */
  325.     int
  326. win_split(new_height, redraw, req_room)
  327.     int        new_height;
  328.     int        redraw;
  329.     int        req_room;        /* require enough room for new window */
  330. {
  331.     WIN        *wp;
  332.     int        i;
  333.     int        need_status;
  334.     int        do_equal = (p_ea && new_height == 0);
  335.     int        needed;
  336.     int        available;
  337.     int        curwin_height;
  338.  
  339.     /* add a status line when p_ls == 1 and splitting the first window */
  340.     if (lastwin == firstwin && p_ls == 1 && curwin->w_status_height == 0)
  341.     need_status = STATUS_HEIGHT;
  342.     else
  343.     need_status = 0;
  344.  
  345. /*
  346.  * check if we are able to split the current window and compute its height
  347.  */
  348.     available = curwin->w_height;
  349.     needed = 2 * MIN_ROWS + STATUS_HEIGHT + need_status;
  350.     if (req_room)
  351.     needed += (p_wh ? (p_wh - 1) : 4);
  352.     if (p_ea)
  353.     {
  354.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  355.         if (wp != curwin)
  356.         {
  357.         available += wp->w_height;
  358.         needed += MIN_ROWS;
  359.         }
  360.     }
  361.     if (available < needed)
  362.     {
  363.     EMSG(e_noroom);
  364.     return FAIL;
  365.     }
  366.     curwin_height = curwin->w_height;
  367.     if (need_status)
  368.     {
  369.     curwin->w_status_height = STATUS_HEIGHT;
  370.     curwin_height -= STATUS_HEIGHT;
  371.     }
  372.     if (new_height == 0)
  373.     new_height = curwin_height / 2;
  374.  
  375.     if (new_height > curwin_height - MIN_ROWS - STATUS_HEIGHT)
  376.     new_height = curwin_height - MIN_ROWS - STATUS_HEIGHT;
  377.  
  378.     if (new_height < MIN_ROWS)
  379.     new_height = MIN_ROWS;
  380.  
  381.     /* if it doesn't fit in the current window, need win_equal() */
  382.     if (curwin_height - new_height - STATUS_HEIGHT < MIN_ROWS)
  383.     do_equal = TRUE;
  384. /*
  385.  * allocate new window structure and link it in the window list
  386.  */
  387.     if (p_sb)        /* new window below current one */
  388.     wp = win_alloc(curwin);
  389.     else
  390.     wp = win_alloc(curwin->w_prev);
  391.     if (wp == NULL)
  392.     return FAIL;
  393. /*
  394.  * compute the new screen positions
  395.  */
  396.     win_new_height(wp, new_height);
  397.     win_new_height(curwin, curwin_height - (new_height + STATUS_HEIGHT));
  398.     if (p_sb)        /* new window below current one */
  399.     {
  400.     wp->w_winpos = curwin->w_winpos + curwin->w_height + STATUS_HEIGHT;
  401.     wp->w_status_height = curwin->w_status_height;
  402.     curwin->w_status_height = STATUS_HEIGHT;
  403.     }
  404.     else        /* new window above current one */
  405.     {
  406.     wp->w_winpos = curwin->w_winpos;
  407.     wp->w_status_height = STATUS_HEIGHT;
  408.     curwin->w_winpos = wp->w_winpos + wp->w_height + STATUS_HEIGHT;
  409.     }
  410. /*
  411.  * make the contents of the new window the same as the current one
  412.  */
  413.     wp->w_buffer = curbuf;
  414.     curbuf->b_nwindows++;
  415.     wp->w_cursor = curwin->w_cursor;
  416.     wp->w_valid = 0;
  417.     wp->w_curswant = curwin->w_curswant;
  418.     wp->w_set_curswant = curwin->w_set_curswant;
  419.     wp->w_topline = curwin->w_topline;
  420.     wp->w_leftcol = curwin->w_leftcol;
  421.     wp->w_pcmark = curwin->w_pcmark;
  422.     wp->w_prev_pcmark = curwin->w_prev_pcmark;
  423.     wp->w_alt_fnum = curwin->w_alt_fnum;
  424.     wp->w_fraction = curwin->w_fraction;
  425.     wp->w_prev_fraction_row = curwin->w_prev_fraction_row;
  426.  
  427.     wp->w_arg_idx = curwin->w_arg_idx;
  428.     /*
  429.      * copy tagstack and options from existing window
  430.      */
  431.     for (i = 0; i < curwin->w_tagstacklen; i++)
  432.     {
  433.     wp->w_tagstack[i].fmark = curwin->w_tagstack[i].fmark;
  434.     wp->w_tagstack[i].tagname = vim_strsave(curwin->w_tagstack[i].tagname);
  435.     }
  436.     wp->w_tagstackidx = curwin->w_tagstackidx;
  437.     wp->w_tagstacklen = curwin->w_tagstacklen;
  438.     win_copy_options(curwin, wp);
  439. /*
  440.  * Both windows need redrawing
  441.  */
  442.     wp->w_redr_type = NOT_VALID;
  443.     wp->w_redr_status = TRUE;
  444.     curwin->w_redr_type = NOT_VALID;
  445.     curwin->w_redr_status = TRUE;
  446.  
  447.     if (need_status)
  448.     {
  449.     msg_row = Rows - 1;
  450.     msg_col = sc_col;
  451.     msg_clr_eos();    /* Old command/ruler may still be there -- webb */
  452.     comp_col();
  453.     msg_row = Rows - 1;
  454.     msg_col = 0;    /* put position back at start of line */
  455.     }
  456. /*
  457.  * make the new window the current window and redraw
  458.  */
  459.     if (do_equal)
  460.     win_equal(wp, FALSE);
  461.     win_enter(wp, FALSE);
  462.  
  463.     if (redraw)
  464.     update_screen(NOT_VALID);
  465.     else
  466.     redraw_later(NOT_VALID);
  467.  
  468.     return OK;
  469. }
  470.  
  471. /*
  472.  * Check if "win" is a pointer to an existing window.
  473.  */
  474.     int
  475. win_valid(win)
  476.     WIN        *win;
  477. {
  478.     WIN        *wp;
  479.  
  480.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  481.     if (wp == win)
  482.         return TRUE;
  483.     return FALSE;
  484. }
  485.  
  486. /*
  487.  * Return the number of windows.
  488.  */
  489.     int
  490. win_count()
  491. {
  492.     WIN        *wp;
  493.     int        count = 0;
  494.  
  495.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  496.     ++count;
  497.     return count;
  498. }
  499.  
  500. /*
  501.  * Make 'count' windows on the screen.
  502.  * Return actual number of windows on the screen.
  503.  * Must be called when there is just one window, filling the whole screen
  504.  * (excluding the command line).
  505.  */
  506.     int
  507. make_windows(count)
  508.     int        count;
  509. {
  510.     int        maxcount;
  511.     int        todo;
  512.     int        p_sb_save;
  513.  
  514. /*
  515.  * Each window needs at least MIN_ROWS lines and a status line.
  516.  * Add 4 lines for one window, otherwise we may end up with all one-line
  517.  * windows. Use value of 'winheight' if it is set
  518.  */
  519.     maxcount = (curwin->w_height + curwin->w_status_height -
  520.             (p_wh ? (p_wh - 1) : 4)) / (MIN_ROWS + STATUS_HEIGHT);
  521.     if (maxcount < 2)
  522.     maxcount = 2;
  523.     if (count > maxcount)
  524.     count = maxcount;
  525.  
  526.     /*
  527.      * add status line now, otherwise first window will be too big
  528.      */
  529.     if ((p_ls == 2 || (count > 1 && p_ls == 1)) && curwin->w_status_height == 0)
  530.     {
  531.     curwin->w_status_height = STATUS_HEIGHT;
  532.     win_new_height(curwin, curwin->w_height - STATUS_HEIGHT);
  533.     }
  534.  
  535. #ifdef AUTOCMD
  536. /*
  537.  * Don't execute autocommands while creating the windows.  Must do that
  538.  * when putting the buffers in the windows.
  539.  */
  540.     ++autocmd_busy;
  541. #endif
  542.  
  543. /*
  544.  * set 'splitbelow' off for a moment, don't want that now
  545.  */
  546.     p_sb_save = p_sb;
  547.     p_sb = FALSE;
  548.     /* todo is number of windows left to create */
  549.     for (todo = count - 1; todo > 0; --todo)
  550.     if (win_split(curwin->w_height - (curwin->w_height - todo
  551.                 * STATUS_HEIGHT) / (todo + 1) - STATUS_HEIGHT,
  552.                             FALSE, FALSE) == FAIL)
  553.         break;
  554.     p_sb = p_sb_save;
  555.  
  556. #ifdef AUTOCMD
  557.     --autocmd_busy;
  558. #endif
  559.  
  560.     /* return actual number of windows */
  561.     return (count - todo);
  562. }
  563.  
  564. /*
  565.  * Exchange current and next window
  566.  */
  567.     static void
  568. win_exchange(Prenum)
  569.     long    Prenum;
  570. {
  571.     WIN        *wp;
  572.     WIN        *wp2;
  573.     int        temp;
  574.  
  575.     if (lastwin == firstwin)        /* just one window */
  576.     {
  577.     beep_flush();
  578.     return;
  579.     }
  580.  
  581. /*
  582.  * find window to exchange with
  583.  */
  584.     if (Prenum)
  585.     {
  586.     wp = firstwin;
  587.     while (wp != NULL && --Prenum > 0)
  588.         wp = wp->w_next;
  589.     }
  590.     else if (curwin->w_next != NULL)    /* Swap with next */
  591.     wp = curwin->w_next;
  592.     else    /* Swap last window with previous */
  593.     wp = curwin->w_prev;
  594.  
  595.     if (wp == curwin || wp == NULL)
  596.     return;
  597.  
  598. /*
  599.  * 1. remove curwin from the list. Remember after which window it was in wp2
  600.  * 2. insert curwin before wp in the list
  601.  * if wp != wp2
  602.  *    3. remove wp from the list
  603.  *    4. insert wp after wp2
  604.  * 5. exchange the status line height
  605.  */
  606.     wp2 = curwin->w_prev;
  607.     win_remove(curwin);
  608.     win_append(wp->w_prev, curwin);
  609.     if (wp != wp2)
  610.     {
  611.     win_remove(wp);
  612.     win_append(wp2, wp);
  613.     }
  614.     temp = curwin->w_status_height;
  615.     curwin->w_status_height = wp->w_status_height;
  616.     wp->w_status_height = temp;
  617.  
  618.     win_comp_pos();        /* recompute window positions */
  619.  
  620.     win_enter(wp, TRUE);
  621.     update_screen(CLEAR);
  622. }
  623.  
  624. /*
  625.  * rotate windows: if upwards TRUE the second window becomes the first one
  626.  *           if upwards FALSE the first window becomes the second one
  627.  */
  628.     static void
  629. win_rotate(upwards, count)
  630.     int        upwards;
  631.     int        count;
  632. {
  633.     WIN         *wp;
  634.     int         height;
  635.  
  636.     if (firstwin == lastwin)        /* nothing to do */
  637.     {
  638.     beep_flush();
  639.     return;
  640.     }
  641.     while (count--)
  642.     {
  643.     if (upwards)        /* first window becomes last window */
  644.     {
  645.         wp = firstwin;
  646.         win_remove(wp);
  647.         win_append(lastwin, wp);
  648.         wp = lastwin->w_prev;        /* previously last window */
  649.     }
  650.     else            /* last window becomes first window */
  651.     {
  652.         wp = lastwin;
  653.         win_remove(lastwin);
  654.         win_append(NULL, wp);
  655.         wp = firstwin;            /* previously last window */
  656.     }
  657.         /* exchange status height of old and new last window */
  658.     height = lastwin->w_status_height;
  659.     lastwin->w_status_height = wp->w_status_height;
  660.     wp->w_status_height = height;
  661.  
  662.         /* recompute w_winpos for all windows */
  663.     (void)win_comp_pos();
  664.     }
  665.  
  666.     update_screen(CLEAR);
  667. }
  668.  
  669. /*
  670.  * Move window "win1" to below "win2" and make "win1" the current window.
  671.  */
  672.     void
  673. win_move_after(win1, win2)
  674.     WIN    *win1, *win2;
  675. {
  676.     int        height;
  677.  
  678.     /* check if the arguments are reasonable */
  679.     if (win1 == win2)
  680.     return;
  681.  
  682.     /* check if there is something to do */
  683.     if (win2->w_next != win1)
  684.     {
  685.     /* may need move the status line of the last window */
  686.     if (win1 == lastwin)
  687.     {
  688.         height = win1->w_prev->w_status_height;
  689.         win1->w_prev->w_status_height = win1->w_status_height;
  690.         win1->w_status_height = height;
  691.     }
  692.     else if (win2 == lastwin)
  693.     {
  694.         height = win1->w_status_height;
  695.         win1->w_status_height = win2->w_status_height;
  696.         win2->w_status_height = height;
  697.     }
  698.     win_remove(win1);
  699.     win_append(win2, win1);
  700.  
  701.     (void)win_comp_pos();    /* recompute w_winpos for all windows */
  702.     redraw_later(NOT_VALID);
  703.     }
  704.     win_enter(win1, FALSE);
  705. }
  706.  
  707. /*
  708.  * Make all windows the same height.
  709.  * 'next_curwin' will soon be the current window, make sure it has enough
  710.  * rows.
  711.  */
  712.     void
  713. win_equal(next_curwin, redraw)
  714.     WIN        *next_curwin;        /* pointer to current window to be */
  715.     int        redraw;
  716. {
  717.     int        total;
  718.     int        less;
  719.     int        wincount;
  720.     int        winpos;
  721.     int        temp;
  722.     WIN        *wp;
  723.     int        new_height;
  724.  
  725. /*
  726.  * count the number of lines available
  727.  */
  728.     total = 0;
  729.     wincount = 0;
  730.     for (wp = firstwin; wp; wp = wp->w_next)
  731.     {
  732.     total += wp->w_height - MIN_ROWS;
  733.     wincount++;
  734.     }
  735.  
  736. /*
  737.  * If next_curwin given and 'winheight' set, make next_curwin p_wh lines.
  738.  */
  739.     less = 0;
  740.     if (next_curwin != NULL)
  741.     {
  742.     if (p_wh)
  743.     {
  744.         if (p_wh - MIN_ROWS > total)    /* all lines go to current window */
  745.         less = total;
  746.         else
  747.         {
  748.         less = p_wh - MIN_ROWS - total / wincount;
  749.         if (less < 0)
  750.             less = 0;
  751.         }
  752.     }
  753.     }
  754.  
  755. /*
  756.  * spread the available lines over the windows
  757.  */
  758.     winpos = 0;
  759.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  760.     {
  761.     if (wp == next_curwin && less)
  762.     {
  763.         less = 0;
  764.         temp = p_wh - MIN_ROWS;
  765.         if (temp > total)
  766.         temp = total;
  767.     }
  768.     else
  769.         temp = (total - less + ((unsigned)wincount >> 1)) / wincount;
  770.     new_height = MIN_ROWS + temp;
  771.     if (wp->w_winpos != winpos || wp->w_height != new_height)
  772.     {
  773.         wp->w_redr_type = NOT_VALID;
  774.         wp->w_redr_status = TRUE;
  775.     }
  776.     wp->w_winpos = winpos;
  777.     win_new_height(wp, new_height);
  778.     total -= temp;
  779.     --wincount;
  780.     winpos += wp->w_height + wp->w_status_height;
  781.     }
  782.     if (redraw)
  783.     must_redraw = CLEAR;
  784. }
  785.  
  786. /*
  787.  * close all windows for buffer 'buf'
  788.  */
  789.     void
  790. close_windows(buf)
  791.     BUF        *buf;
  792. {
  793.     WIN        *win;
  794.  
  795.     ++RedrawingDisabled;
  796.     for (win = firstwin; win != NULL && lastwin != firstwin; )
  797.     {
  798.     if (win->w_buffer == buf)
  799.     {
  800.         close_window(win, FALSE);
  801.         win = firstwin;        /* go back to the start */
  802.     }
  803.     else
  804.         win = win->w_next;
  805.     }
  806.     --RedrawingDisabled;
  807. }
  808.  
  809. /*
  810.  * close window "win"
  811.  * If "free_buf" is TRUE related buffer may be freed.
  812.  *
  813.  * called by :quit, :close, :xit, :wq and findtag()
  814.  */
  815.     void
  816. close_window(win, free_buf)
  817.     WIN        *win;
  818.     int        free_buf;
  819. {
  820.     WIN        *wp;
  821. #ifdef AUTOCMD
  822.     int        other_buffer = FALSE;
  823. #endif
  824.  
  825.     if (lastwin == firstwin)
  826.     {
  827.     EMSG("Cannot close last window");
  828.     return;
  829.     }
  830.  
  831. #ifdef AUTOCMD
  832.     if (win == curwin)
  833.     {
  834.     /*
  835.      * Guess which window is going to be the new current window.
  836.      * This may change because of the autocommands (sigh).
  837.      */
  838.     if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
  839.         wp = win->w_next;
  840.     else
  841.         wp = win->w_prev;
  842.  
  843.     /*
  844.      * Be careful: If autocommands delete the window, return now.
  845.      */
  846.     if (wp->w_buffer != curbuf)
  847.     {
  848.         other_buffer = TRUE;
  849.         apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE);
  850.         if (!win_valid(win))
  851.         return;
  852.     }
  853.     apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE);
  854.     if (!win_valid(win))
  855.         return;
  856.     }
  857. #endif
  858.  
  859. /*
  860.  * Remove the window.
  861.  * if 'splitbelow' the free space goes to the window above it.
  862.  * if 'nosplitbelow' the free space goes to the window below it.
  863.  * This makes opening a window and closing it immediately keep the same window
  864.  * layout.
  865.  */
  866.                     /* freed space goes to next window */
  867.     if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
  868.     {
  869.     wp = win->w_next;
  870.     wp->w_winpos = win->w_winpos;
  871.     }
  872.     else                /* freed space goes to previous window */
  873.     wp = win->w_prev;
  874.     win_new_height(wp, wp->w_height + win->w_height + win->w_status_height);
  875.  
  876. /*
  877.  * Close the link to the buffer.
  878.  */
  879.     close_buffer(win, win->w_buffer, free_buf, FALSE);
  880.  
  881.     win_free(win);
  882.     if (win == curwin)
  883.     curwin = NULL;
  884.     if (p_ea)
  885.     win_equal(wp, FALSE);
  886.     if (curwin == NULL)
  887.     {
  888.     win_enter(wp, FALSE);
  889. #ifdef AUTOCMD
  890.     if (other_buffer)
  891.         /* careful: after this wp and win may be invalid! */
  892.         apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE);
  893. #endif
  894.     }
  895.  
  896.     /*
  897.      * if last window has status line now and we don't want one,
  898.      * remove the status line
  899.      */
  900.     if (lastwin->w_status_height &&
  901.             (p_ls == 0 || (p_ls == 1 && firstwin == lastwin)))
  902.     {
  903.     win_new_height(lastwin, lastwin->w_height + lastwin->w_status_height);
  904.     lastwin->w_status_height = 0;
  905.     comp_col();
  906.     }
  907.  
  908.     update_screen(NOT_VALID);
  909. }
  910.  
  911. /*
  912.  * Close all windows except current one.
  913.  * Buffers in the other windows become hidden if 'hidden' is set, or '!' is
  914.  * used and the buffer was modified.
  915.  *
  916.  * Used by ":bdel" and ":only".
  917.  */
  918.     void
  919. close_others(message, forceit)
  920.     int        message;
  921.     int        forceit;        /* always hide all other windows */
  922. {
  923.     WIN        *wp;
  924.     WIN        *nextwp;
  925.  
  926.     if (lastwin == firstwin)
  927.     {
  928.     if (message
  929. #ifdef AUTOCMD
  930.             && !autocmd_busy
  931. #endif
  932.                     )
  933.         MSG("Already only one window");
  934.     return;
  935.     }
  936.  
  937.     for (wp = firstwin; wp != NULL; wp = nextwp)
  938.     {
  939.     nextwp = wp->w_next;
  940.     if (wp == curwin)        /* don't close current window */
  941.         continue;
  942.  
  943.     /* Check if it's allowed to abandon this window */
  944.     if (!can_abandon(wp->w_buffer, forceit))
  945.         continue;
  946.  
  947.     /* Close the link to the buffer. */
  948.     close_buffer(wp, wp->w_buffer,
  949.                  !p_hid && !buf_changed(wp->w_buffer), FALSE);
  950.  
  951.     /* Remove the window.  All lines go to previous or next window. */
  952.     if (wp->w_prev != NULL)
  953.         win_new_height(wp->w_prev,
  954.            wp->w_prev->w_height + wp->w_height + wp->w_status_height);
  955.     else
  956.     {
  957.         win_new_height(wp->w_next,
  958.            wp->w_next->w_height + wp->w_height + wp->w_status_height);
  959.         wp->w_next->w_winpos = wp->w_winpos;
  960.     }
  961.     win_free(wp);
  962.     }
  963.  
  964.     /*
  965.      * If current window has a status line and we don't want one,
  966.      * remove the status line.
  967.      */
  968.     if (lastwin != firstwin)
  969.     EMSG("Other window contains changes");
  970.     else if (curwin->w_status_height && p_ls != 2)
  971.     {
  972.     win_new_height(curwin, curwin->w_height + curwin->w_status_height);
  973.     curwin->w_status_height = 0;
  974.     }
  975.     if (message)
  976.     update_screen(NOT_VALID);
  977. }
  978.  
  979. /*
  980.  * init the cursor in the window
  981.  *
  982.  * called when a new file is being edited
  983.  */
  984.     void
  985. win_init(wp)
  986.     WIN        *wp;
  987. {
  988.     wp->w_redr_type = NOT_VALID;
  989.     wp->w_cursor.lnum = 1;
  990.     wp->w_curswant = wp->w_cursor.col = 0;
  991.     wp->w_pcmark.lnum = 1;    /* pcmark not cleared but set to line 1 */
  992.     wp->w_pcmark.col = 0;
  993.     wp->w_prev_pcmark.lnum = 0;
  994.     wp->w_prev_pcmark.col = 0;
  995.     wp->w_topline = 1;
  996.     wp->w_botline = 2;
  997. #ifdef FKMAP
  998.     if (curwin->w_p_rl)
  999.     wp->w_p_pers = W_CONV + W_R_L;
  1000.     else
  1001.     wp->w_p_pers = W_CONV;
  1002. #endif
  1003. }
  1004.  
  1005. /*
  1006.  * Go to another window.
  1007.  * When jumping to another buffer, stop visual mode.  Do this before
  1008.  * changing windows so we can yank the selection into the '*' register.
  1009.  */
  1010.     static void
  1011. win_goto(wp)
  1012.     WIN        *wp;
  1013. {
  1014.     if (wp->w_buffer != curbuf && VIsual_active)
  1015.     {
  1016.     end_visual_mode();
  1017.     redraw_curbuf_later(NOT_VALID);
  1018.     }
  1019.     win_enter(wp, TRUE);
  1020. }
  1021.  
  1022. /*
  1023.  * Go to window nr "winnr" (counting top to bottom).
  1024.  */
  1025.     WIN *
  1026. win_goto_nr(winnr)
  1027.     int        winnr;
  1028. {
  1029.     WIN        *wp;
  1030.  
  1031.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1032.     if (--winnr == 0)
  1033.         break;
  1034.     return wp;
  1035. }
  1036.  
  1037. /*
  1038.  * Make window wp the current window.
  1039.  * Can be called when curwin == NULL, if curwin already has been closed.
  1040.  */
  1041.     void
  1042. win_enter(wp, undo_sync)
  1043.     WIN        *wp;
  1044.     int        undo_sync;
  1045. {
  1046. #ifdef AUTOCMD
  1047.     int        other_buffer = FALSE;
  1048. #endif
  1049.  
  1050.     if (wp == curwin)        /* nothing to do */
  1051.     return;
  1052.  
  1053. #ifdef AUTOCMD
  1054.     if (curwin != NULL)
  1055.     {
  1056.     /*
  1057.      * Be careful: If autocommands delete the window, return now.
  1058.      */
  1059.     if (wp->w_buffer != curbuf)
  1060.     {
  1061.         apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE);
  1062.         other_buffer = TRUE;
  1063.         if (!win_valid(wp))
  1064.         return;
  1065.     }
  1066.     apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE);
  1067.     if (!win_valid(wp))
  1068.         return;
  1069.     }
  1070. #endif
  1071.  
  1072.     /* sync undo before leaving the current buffer */
  1073.     if (undo_sync && curbuf != wp->w_buffer)
  1074.     u_sync();
  1075.     /* may have to copy the buffer options when 'cpo' contains 'S' */
  1076.     if (wp->w_buffer != curbuf)
  1077.     buf_copy_options(curbuf, wp->w_buffer, BCO_ENTER | BCO_NOHELP);
  1078.     if (curwin != NULL)
  1079.     {
  1080.     prevwin = curwin;    /* remember for CTRL-W p */
  1081.     curwin->w_redr_status = TRUE;
  1082.     }
  1083.     curwin = wp;
  1084.     curbuf = wp->w_buffer;
  1085.     changed_line_abv_curs();    /* assume cursor position needs updating */
  1086.  
  1087. #ifdef AUTOCMD
  1088.     apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE);
  1089.     if (other_buffer)
  1090.     apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE);
  1091. #endif
  1092.  
  1093.     maketitle();
  1094.     curwin->w_redr_status = TRUE;
  1095.  
  1096.         /* set window height to desired minimal value */
  1097.     if (p_wh && curwin->w_height < p_wh)
  1098.     win_setheight((int)p_wh);
  1099. #ifdef USE_MOUSE
  1100.     setmouse();            /* in case jumped to/from help buffer */
  1101. #endif
  1102. }
  1103.  
  1104. /*
  1105.  * allocate a window structure and link it in the window list
  1106.  */
  1107.     WIN *
  1108. win_alloc(after)
  1109.     WIN        *after;
  1110. {
  1111.     WIN        *newwin;
  1112.  
  1113.     /*
  1114.      * allocate window structure and linesizes arrays
  1115.      */
  1116.     newwin = (WIN *)alloc_clear((unsigned)sizeof(WIN));
  1117.     if (newwin != NULL && win_alloc_lsize(newwin) == FAIL)
  1118.     {
  1119.     vim_free(newwin);
  1120.     newwin = NULL;
  1121.     }
  1122.  
  1123.     if (newwin != NULL)
  1124.     {
  1125.     /*
  1126.      * link the window in the window list
  1127.      */
  1128.     win_append(after, newwin);
  1129.  
  1130.     /* position the display and the cursor at the top of the file. */
  1131.     newwin->w_topline = 1;
  1132.     newwin->w_botline = 2;
  1133.     newwin->w_cursor.lnum = 1;
  1134.  
  1135.     /* We won't calculate w_fraction until resizing the window */
  1136.     newwin->w_fraction = 0;
  1137.     newwin->w_prev_fraction_row = -1;
  1138.  
  1139. #ifdef USE_GUI
  1140.     if (gui.in_use)
  1141.     {
  1142.         gui_create_scrollbar(&newwin->w_scrollbars[SBAR_LEFT], newwin);
  1143.         gui_create_scrollbar(&newwin->w_scrollbars[SBAR_RIGHT], newwin);
  1144.     }
  1145. #endif
  1146. #ifdef WANT_EVAL
  1147.     var_init(&newwin->w_vars);        /* init internal variables */
  1148. #endif
  1149.     }
  1150.     return newwin;
  1151. }
  1152.  
  1153. /*
  1154.  * remove window 'wp' from the window list and free the structure
  1155.  */
  1156.     void
  1157. win_free(wp)
  1158.     WIN        *wp;
  1159. {
  1160.     int        i;
  1161.  
  1162. #ifdef HAVE_PERL_INTERP
  1163.     perl_win_free(wp);
  1164. #endif
  1165.  
  1166. #ifdef HAVE_PYTHON
  1167.     python_window_free(wp);
  1168. #endif
  1169.  
  1170. #ifdef WANT_EVAL
  1171.     var_clear(&wp->w_vars);        /* free all internal variables */
  1172. #endif
  1173.  
  1174.     if (prevwin == wp)
  1175.     prevwin = NULL;
  1176.     win_free_lsize(wp);
  1177.  
  1178.     for (i = 0; i < wp->w_tagstacklen; ++i)
  1179.     vim_free(wp->w_tagstack[i].tagname);
  1180.  
  1181. #ifdef USE_GUI
  1182.     if (gui.in_use)
  1183.     {
  1184.     gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]);
  1185.     gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]);
  1186.     }
  1187. #endif /* USE_GUI */
  1188.  
  1189.     win_remove(wp);
  1190.     vim_free(wp);
  1191. }
  1192.  
  1193.     static void
  1194. win_append(after, wp)
  1195.     WIN        *after, *wp;
  1196. {
  1197.     WIN        *before;
  1198.  
  1199.     if (after == NULL)        /* after NULL is in front of the first */
  1200.     before = firstwin;
  1201.     else
  1202.     before = after->w_next;
  1203.  
  1204.     wp->w_next = before;
  1205.     wp->w_prev = after;
  1206.     if (after == NULL)
  1207.     firstwin = wp;
  1208.     else
  1209.     after->w_next = wp;
  1210.     if (before == NULL)
  1211.     lastwin = wp;
  1212.     else
  1213.     before->w_prev = wp;
  1214. }
  1215.  
  1216. /*
  1217.  * remove window from the window list
  1218.  */
  1219.     static void
  1220. win_remove(wp)
  1221.     WIN        *wp;
  1222. {
  1223.     if (wp->w_prev)
  1224.     wp->w_prev->w_next = wp->w_next;
  1225.     else
  1226.     firstwin = wp->w_next;
  1227.     if (wp->w_next)
  1228.     wp->w_next->w_prev = wp->w_prev;
  1229.     else
  1230.     lastwin = wp->w_prev;
  1231. }
  1232.  
  1233. /*
  1234.  * allocate lsize arrays for a window
  1235.  * return FAIL for failure, OK for success
  1236.  */
  1237.     int
  1238. win_alloc_lsize(wp)
  1239.     WIN        *wp;
  1240. {
  1241.     wp->w_lsize_valid = 0;
  1242.     wp->w_lsize_lnum = (linenr_t *)alloc((unsigned)(Rows * sizeof(linenr_t)));
  1243.     wp->w_lsize = alloc((unsigned)Rows);
  1244.     if (wp->w_lsize_lnum == NULL || wp->w_lsize == NULL)
  1245.     {
  1246.     win_free_lsize(wp);    /* one of the two may have worked */
  1247.     return FAIL;
  1248.     }
  1249.     return OK;
  1250. }
  1251.  
  1252. /*
  1253.  * free lsize arrays for a window
  1254.  */
  1255.     void
  1256. win_free_lsize(wp)
  1257.     WIN        *wp;
  1258. {
  1259.     vim_free(wp->w_lsize_lnum);
  1260.     vim_free(wp->w_lsize);
  1261.     wp->w_lsize_lnum = NULL;
  1262.     wp->w_lsize = NULL;
  1263. }
  1264.  
  1265. /*
  1266.  * call this fuction whenever Rows changes value
  1267.  */
  1268.     void
  1269. screen_new_rows()
  1270. {
  1271.     WIN        *wp;
  1272.     int        extra_lines;
  1273.  
  1274.     if (firstwin == NULL)    /* not initialized yet */
  1275.     return;
  1276. /*
  1277.  * the number of extra lines is the difference between the position where
  1278.  * the command line should be and where it is now
  1279.  */
  1280.     extra_lines = Rows - p_ch -
  1281.        (lastwin->w_winpos + lastwin->w_height + lastwin->w_status_height);
  1282.     if (extra_lines < 0)            /* reduce windows height */
  1283.     {
  1284.     for (wp = lastwin; wp; wp = wp->w_prev)
  1285.     {
  1286.         if (wp->w_height - MIN_ROWS < -extra_lines)
  1287.         {
  1288.         extra_lines += wp->w_height - MIN_ROWS;
  1289.         win_new_height(wp, MIN_ROWS);
  1290.         }
  1291.         else
  1292.         {
  1293.         win_new_height(wp, wp->w_height + extra_lines);
  1294.         break;
  1295.         }
  1296.     }
  1297.     (void)win_comp_pos();            /* compute w_winpos */
  1298.     }
  1299.     else if (extra_lines > 0)            /* increase height of last window */
  1300.     win_new_height(lastwin, lastwin->w_height + extra_lines);
  1301.  
  1302.     compute_cmdrow();
  1303.  
  1304.     if (p_ea)
  1305.     win_equal(curwin, FALSE);
  1306. }
  1307.  
  1308. /*
  1309.  * update the w_winpos field for all windows
  1310.  * returns the row just after the last window
  1311.  */
  1312.     static int
  1313. win_comp_pos()
  1314. {
  1315.     WIN        *wp;
  1316.     int        row;
  1317.  
  1318.     row = 0;
  1319.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1320.     {
  1321.     if (wp->w_winpos != row)    /* if position changes, redraw */
  1322.     {
  1323.         wp->w_winpos = row;
  1324.         wp->w_redr_type = NOT_VALID;
  1325.         wp->w_redr_status = TRUE;
  1326.     }
  1327.     row += wp->w_height + wp->w_status_height;
  1328.     }
  1329.     return row;
  1330. }
  1331.  
  1332. /*
  1333.  * set current window height
  1334.  */
  1335.     void
  1336. win_setheight(height)
  1337.     int        height;
  1338. {
  1339.     WIN        *wp;
  1340.     int        room;        /* total number of lines available */
  1341.     int        take;        /* number of lines taken from other windows */
  1342.     int        room_cmdline;    /* lines available from cmdline */
  1343.     int        row;
  1344.     int        run;
  1345.  
  1346.     if (height < MIN_ROWS)    /* need at least some lines */
  1347.     height = MIN_ROWS;
  1348. /*
  1349.  * compute the room we have from all the windows
  1350.  */
  1351.     room = MIN_ROWS;        /* count the MIN_ROWS for the current window */
  1352.     room_cmdline = Rows - p_ch;
  1353.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1354.     {
  1355.     room += wp->w_height - MIN_ROWS;
  1356.     room_cmdline -= wp->w_height + wp->w_status_height;
  1357.     }
  1358. /*
  1359.  * limit new height to the room available
  1360.  */
  1361.     if (height > room + room_cmdline)        /* can't make it that large */
  1362.     height = room + room_cmdline;        /* use all available room */
  1363. /*
  1364.  * compute the number of lines we will take from the windows (can be negative)
  1365.  */
  1366.     take = height - curwin->w_height;
  1367.     if (take == 0)                /* no change, nothing to do */
  1368.     return;
  1369.  
  1370.     if (take > 0)
  1371.     {
  1372.     take -= room_cmdline;            /* use lines from cmdline first */
  1373.     if (take < 0)
  1374.         take = 0;
  1375.     }
  1376. /*
  1377.  * set the current window to the new height
  1378.  */
  1379.     win_new_height(curwin, height);
  1380.  
  1381. /*
  1382.  * First take lines from the windows below the current window.
  1383.  * If that is not enough, takes lines from windows above the current window.
  1384.  */
  1385.     for (run = 0; run < 2; ++run)
  1386.     {
  1387.     if (run == 0)
  1388.         wp = curwin->w_next;    /* 1st run: start with next window */
  1389.     else
  1390.         wp = curwin->w_prev;    /* 2nd run: start with prev window */
  1391.     while (wp != NULL && take != 0)
  1392.     {
  1393.         if (wp->w_height - take < MIN_ROWS)
  1394.         {
  1395.         take -= wp->w_height - MIN_ROWS;
  1396.         win_new_height(wp, MIN_ROWS);
  1397.         }
  1398.         else
  1399.         {
  1400.         win_new_height(wp, wp->w_height - take);
  1401.         take = 0;
  1402.         }
  1403.         if (run == 0)
  1404.         wp = wp->w_next;
  1405.         else
  1406.         wp = wp->w_prev;
  1407.     }
  1408.     }
  1409.  
  1410. /* recompute the window positions */
  1411.     row = win_comp_pos();
  1412.  
  1413. /*
  1414.  * If there is extra space created between the last window and the command line,
  1415.  * clear it.
  1416.  */
  1417.     if (full_screen && msg_scrolled == 0)
  1418.     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
  1419.     cmdline_row = row;
  1420.  
  1421.     update_screen(NOT_VALID);
  1422. }
  1423.  
  1424. #ifdef USE_MOUSE
  1425.     void
  1426. win_drag_status_line(offset)
  1427.     int        offset;
  1428. {
  1429.     WIN        *wp;
  1430.     int        room;
  1431.     int        row;
  1432.     int        up;            /* if TRUE, drag status line up, otherwise down */
  1433.  
  1434.     if (offset < 0)
  1435.     {
  1436.     up = TRUE;
  1437.     offset = -offset;
  1438.     }
  1439.     else
  1440.     up = FALSE;
  1441.  
  1442.     if (up) /* drag up */
  1443.     {
  1444.     room = 0;
  1445.     for (wp = curwin; wp != NULL && room < offset; wp = wp->w_prev)
  1446.         room += wp->w_height - MIN_ROWS;
  1447.     wp = curwin->w_next;            /* put wp at window that grows */
  1448.     }
  1449.     else    /* drag down */
  1450.     {
  1451.     /*
  1452.      * Only dragging the last status line can reduce p_ch.
  1453.      */
  1454.     room = Rows - cmdline_row;
  1455.     if (curwin->w_next == NULL)
  1456.         room -= 1;
  1457.     else
  1458.         room -= p_ch;
  1459.     for (wp = curwin->w_next; wp != NULL && room < offset; wp = wp->w_next)
  1460.         room += wp->w_height - MIN_ROWS;
  1461.     wp = curwin;                /* put wp at window that grows */
  1462.     }
  1463.  
  1464.     if (room < offset)        /* Not enough room */
  1465.     offset = room;        /* Move as far as we can */
  1466.     if (offset <= 0)
  1467.     return;
  1468.  
  1469.     if (wp != NULL)        /* grow window wp by offset lines */
  1470.     win_new_height(wp, wp->w_height + offset);
  1471.  
  1472.     if (up)
  1473.     wp = curwin;            /* current window gets smaller */
  1474.     else
  1475.     wp = curwin->w_next;        /* next window gets smaller */
  1476.  
  1477.     while (wp != NULL && offset > 0)
  1478.     {
  1479.     if (wp->w_height - offset < MIN_ROWS)
  1480.     {
  1481.         offset -= wp->w_height - MIN_ROWS;
  1482.         win_new_height(wp, MIN_ROWS);
  1483.     }
  1484.     else
  1485.     {
  1486.         win_new_height(wp, wp->w_height - offset);
  1487.         offset = 0;
  1488.     }
  1489.     if (up)
  1490.         wp = wp->w_prev;
  1491.     else
  1492.         wp = wp->w_next;
  1493.     }
  1494.     row = win_comp_pos();
  1495.     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
  1496.     cmdline_row = row;
  1497.     p_ch = Rows - cmdline_row;
  1498.     update_screen(NOT_VALID);
  1499.     showmode();
  1500. }
  1501. #endif /* USE_MOUSE */
  1502.  
  1503. /*
  1504.  * Set new window height.
  1505.  */
  1506.     static void
  1507. win_new_height(wp, height)
  1508.     WIN        *wp;
  1509.     int        height;
  1510. {
  1511.     linenr_t    lnum;
  1512.     int        sline, line_size;
  1513. #define FRACTION_MULT    16384L
  1514.  
  1515.     if (wp->w_wrow != wp->w_prev_fraction_row && wp->w_height > 0)
  1516.     wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT
  1517.                     + FRACTION_MULT / 2) / (long)wp->w_height;
  1518.  
  1519.     wp->w_height = height;
  1520.  
  1521.     lnum = wp->w_cursor.lnum;
  1522.     if (lnum < 1)        /* can happen when starting up */
  1523.     lnum = 1;
  1524.     wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT;
  1525.     line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
  1526.     sline = wp->w_wrow - line_size;
  1527.     if (sline < 0)
  1528.     {
  1529.     /*
  1530.      * Cursor line would go off top of screen if w_wrow was this high.
  1531.      */
  1532.     wp->w_wrow = line_size;
  1533.     }
  1534.     else
  1535.     {
  1536.     while (sline > 0 && lnum > 1)
  1537.         sline -= (line_size = plines_win(wp, --lnum));
  1538.     if (sline < 0)
  1539.     {
  1540.         /*
  1541.          * Line we want at top would go off top of screen.    Use next line
  1542.          * instead.
  1543.          */
  1544.         lnum++;
  1545.         wp->w_wrow -= line_size + sline;
  1546.     }
  1547.     else if (sline > 0)
  1548.     {
  1549.         /* First line of file reached, use that as topline. */
  1550.         lnum = 1;
  1551.         wp->w_wrow -= sline;
  1552.     }
  1553.     }
  1554.     set_topline(wp, lnum);
  1555.     if (wp == curwin)
  1556.     {
  1557.     if (p_so)
  1558.         update_topline();
  1559.     curs_columns(FALSE);        /* validate w_wrow */
  1560.     }
  1561.     wp->w_prev_fraction_row = wp->w_wrow;
  1562.  
  1563.     win_comp_scroll(wp);
  1564.     if (wp->w_redr_type < NOT_VALID)
  1565.     wp->w_redr_type = NOT_VALID;
  1566.     wp->w_redr_status = TRUE;
  1567.     invalidate_botline_win(wp);
  1568. }
  1569.  
  1570.     void
  1571. win_comp_scroll(wp)
  1572.     WIN        *wp;
  1573. {
  1574.     wp->w_p_scroll = ((unsigned)wp->w_height >> 1);
  1575.     if (wp->w_p_scroll == 0)
  1576.     wp->w_p_scroll = 1;
  1577. }
  1578.  
  1579. /*
  1580.  * command_height: called whenever p_ch has been changed
  1581.  */
  1582.     void
  1583. command_height(old_p_ch)
  1584.     long    old_p_ch;
  1585. {
  1586.     WIN        *wp;
  1587.     int        h;
  1588.  
  1589.     if (!starting)
  1590.     {
  1591.     cmdline_row = Rows - p_ch;
  1592.     if (p_ch > old_p_ch)            /* p_ch got bigger */
  1593.     {
  1594.         for (wp = lastwin; p_ch > old_p_ch; wp = wp->w_prev)
  1595.         {
  1596.         if (wp == NULL)
  1597.         {
  1598.             emsg(e_noroom);
  1599.             p_ch = old_p_ch;
  1600.             break;
  1601.         }
  1602.         h = wp->w_height - (p_ch - old_p_ch);
  1603.         if (h < MIN_ROWS)
  1604.             h = MIN_ROWS;
  1605.         old_p_ch += wp->w_height - h;
  1606.         win_new_height(wp, h);
  1607.         }
  1608.         win_comp_pos();
  1609.         /* clear the lines added to cmdline */
  1610.         if (full_screen)
  1611.         screen_fill((int)(cmdline_row), (int)Rows, 0,
  1612.                            (int)Columns, ' ', ' ', 0);
  1613.         msg_row = cmdline_row;
  1614.         redraw_cmdline = TRUE;
  1615.         return;
  1616.     }
  1617.  
  1618.     if (msg_row < cmdline_row)
  1619.         msg_row = cmdline_row;
  1620.     redraw_cmdline = TRUE;
  1621.     }
  1622.     win_new_height(lastwin, (int)(lastwin->w_height + old_p_ch - p_ch));
  1623. }
  1624.  
  1625.     void
  1626. last_status()
  1627. {
  1628.     if (lastwin->w_status_height)
  1629.     {
  1630.             /* remove status line */
  1631.     if (p_ls == 0 || (p_ls == 1 && firstwin == lastwin))
  1632.     {
  1633.         win_new_height(lastwin, lastwin->w_height + 1);
  1634.         lastwin->w_status_height = 0;
  1635.     }
  1636.     }
  1637.     else
  1638.     {
  1639.     /* add status line */
  1640.     if (p_ls == 2 || (p_ls == 1 && firstwin != lastwin))
  1641.     {
  1642.         if (lastwin->w_height <= MIN_ROWS)        /* can't do it */
  1643.         emsg(e_noroom);
  1644.         else
  1645.         {
  1646.         win_new_height(lastwin, lastwin->w_height - 1);
  1647.         lastwin->w_status_height = 1;
  1648.         }
  1649.     }
  1650.     }
  1651. }
  1652.  
  1653. #ifdef FILE_IN_PATH
  1654. /*
  1655.  * file_name_at_cursor()
  1656.  *
  1657.  * Return the name of the file under (or to the right of) the cursor.
  1658.  *
  1659.  * get_file_name_in_path()
  1660.  *
  1661.  * Return the name of the file at (or to the right of) ptr[col].
  1662.  *
  1663.  * The p_path variable is searched if the file name does not start with '/'.
  1664.  * The string returned has been alloc'ed and should be freed by the caller.
  1665.  * NULL is returned if the file name or file is not found.
  1666.  *
  1667.  * options:
  1668.  * FNAME_MESS        give error messages
  1669.  * FNAME_EXP        expand to path
  1670.  * FNAME_HYP        check for hypertext link
  1671.  */
  1672.     char_u *
  1673. file_name_at_cursor(options, count)
  1674.     int        options;
  1675.     long    count;
  1676. {
  1677.     return get_file_name_in_path(ml_get_curline(),
  1678.                     curwin->w_cursor.col, options, count);
  1679. }
  1680.  
  1681.     char_u *
  1682. get_file_name_in_path(line, col, options, count)
  1683.     char_u  *line;
  1684.     int        col;
  1685.     int        options;
  1686.     long    count;
  1687. {
  1688.     char_u  *ptr;
  1689.     char_u  *file_name;
  1690.     int        len;
  1691.  
  1692.     /*
  1693.      * search forward for what could be the start of a file name
  1694.      */
  1695.     ptr = line + col;
  1696.     while (*ptr != NUL && !vim_isfilec(*ptr))
  1697.     ++ptr;
  1698.     if (*ptr == NUL)        /* nothing found */
  1699.     {
  1700.     if (options & FNAME_MESS)
  1701.         EMSG("No file name under cursor");
  1702.     return NULL;
  1703.     }
  1704.  
  1705.     /*
  1706.      * search backward for first char of the file name
  1707.      */
  1708.     while (ptr > line && vim_isfilec(ptr[-1]))
  1709.     --ptr;
  1710.  
  1711.     /*
  1712.      * Go one char back to ":" before "//" even when ':' is not in 'isfname'.
  1713.      */
  1714.     if ((options & FNAME_HYP) && ptr > line && path_is_url(ptr - 1))
  1715.     --ptr;
  1716.  
  1717.     /*
  1718.      * Search forward for the last char of the file name.
  1719.      * Also allow "://" when ':' is not in 'isfname'.
  1720.      */
  1721.     len = 0;
  1722.     while (vim_isfilec(ptr[len])
  1723.              || ((options & FNAME_HYP) && path_is_url(ptr + len)))
  1724.     ++len;
  1725.  
  1726.     if (options & FNAME_HYP)
  1727.     {
  1728.     /* For hypertext links, ignore the name of the machine.
  1729.      * Such a link looks like "type://machine/path". Only "/path" is used.
  1730.      * First search for the string "://", then for the extra '/'
  1731.      */
  1732.     if ((file_name = vim_strchr(ptr, ':')) != NULL &&
  1733.         ((path_is_url(file_name) == URL_SLASH &&
  1734.           (file_name = vim_strchr(file_name + 3, '/')) != NULL) ||
  1735.          (path_is_url(file_name) == URL_BACKSLASH &&
  1736.           (file_name = vim_strchr(file_name + 3, '\\')) != NULL)) &&
  1737.         file_name < ptr + len)
  1738.     {
  1739.         len -= file_name - ptr;
  1740.         ptr = file_name;
  1741.         if (ptr[1] == '~')        /* skip '/' for /~user/path */
  1742.         {
  1743.         ++ptr;
  1744.         --len;
  1745.         }
  1746.     }
  1747.     }
  1748.  
  1749.     if (!(options & FNAME_EXP))
  1750.     return vim_strnsave(ptr, len);
  1751.  
  1752.     return find_file_in_path(ptr, len, options, count);
  1753. }
  1754.  
  1755. /*
  1756.  * Find the file name "ptr[len]" in the path.
  1757.  *
  1758.  * options:
  1759.  * FNAME_MESS        give error message when not found
  1760.  *
  1761.  * Uses NameBuff[]!
  1762.  *
  1763.  * Returns an allocated string for the file name.  NULL for error.
  1764.  */
  1765.     static char_u *
  1766. find_file_in_path(ptr, len, options, count)
  1767.     char_u    *ptr;        /* file name */
  1768.     int        len;        /* length of file name */
  1769.     int        options;
  1770.     long    count;        /* use count'th matching file name */
  1771. {
  1772.     char_u    save_char;
  1773.     char_u    *file_name;
  1774.     char_u    *curr_path = NULL;
  1775.     char_u    *dir;
  1776.     int        curr_path_len;
  1777.     char_u    *p;
  1778.     char_u    *head;
  1779.  
  1780.  
  1781.     /* copy file name into NameBuff, expanding environment variables */
  1782.     save_char = ptr[len];
  1783.     ptr[len] = NUL;
  1784.     expand_env(ptr, NameBuff, MAXPATHL);
  1785.     ptr[len] = save_char;
  1786.  
  1787.     if (mch_isFullName(NameBuff))
  1788.     {
  1789.     /*
  1790.      * Absolute path, no need to use 'path'.
  1791.      */
  1792.     if ((file_name = vim_strsave(NameBuff)) == NULL)
  1793.         return NULL;
  1794.     if (mch_getperm(file_name) >= 0)
  1795.         return file_name;
  1796.     if (options & FNAME_MESS)
  1797.         EMSG2("Can't find file \"%s\"", NameBuff);
  1798.     }
  1799.     else
  1800.     {
  1801.     /*
  1802.      * Relative path, use 'path' option.
  1803.      */
  1804.     if (curbuf->b_fname != NULL)
  1805.     {
  1806.         curr_path = curbuf->b_fname;
  1807.         ptr = gettail(curr_path);
  1808.         curr_path_len = ptr - curr_path;
  1809.     }
  1810.     else
  1811.         curr_path_len = 0;
  1812.     if ((file_name = alloc((int)(curr_path_len + STRLEN(p_path) +
  1813.                         STRLEN(NameBuff) + 3))) == NULL)
  1814.         return NULL;
  1815.  
  1816.     for (dir = p_path; *dir && !got_int; )
  1817.     {
  1818.         len = copy_option_part(&dir, file_name, 31000, " ,");
  1819.         /* len == 0 means: use current directory */
  1820.         if (len != 0)
  1821.         {
  1822.         /* Look for file relative to current file */
  1823.         if (file_name[0] == '.' && curr_path_len > 0
  1824.                  && (len == 1 || vim_ispathsep(file_name[1])))
  1825.         {
  1826.             if (len == 1)    /* just a "." */
  1827.             len = 0;
  1828.             else        /* "./path": move "path" */
  1829.             {
  1830.             len -= 2;
  1831.             vim_memmove(file_name + curr_path_len, file_name + 2,
  1832.                                  (size_t)len);
  1833.             }
  1834.             STRNCPY(file_name, curr_path, curr_path_len);
  1835.             len += curr_path_len;
  1836.         }
  1837.         if (!vim_ispathsep(file_name[len - 1]))
  1838.             file_name[len++] = PATHSEP;
  1839.         file_name[len] = '\0';
  1840.  
  1841.         /*
  1842.          * Handle "**" in the path: 'wildcard in path'.
  1843.          */
  1844.         if (mch_has_wildcard(file_name))
  1845.         {
  1846.             p = get_past_head(file_name);
  1847.             if (p == file_name)        /* no absolute file name */
  1848.             p = find_file_in_wildcard_path((char_u *)"",
  1849.                             file_name, 0, &count);
  1850.             else    /* absolute file name, separate head */
  1851.             {
  1852.             head = vim_strnsave(file_name,
  1853.                            (unsigned)(p - file_name));
  1854.             if (head != NULL)
  1855.             {
  1856.                 p = find_file_in_wildcard_path(head, p, 0, &count);
  1857.                 vim_free(head);
  1858.             }
  1859.             }
  1860.             if (p != NULL)
  1861.             {
  1862.             vim_free(file_name);
  1863.             return p;
  1864.             }
  1865.             continue;
  1866.         }
  1867.         }
  1868.         STRCPY(file_name + len, NameBuff);
  1869.  
  1870.         /*
  1871.          * Translate names like "src/a/../b/file.c" into "src/b/file.c".
  1872.          */
  1873.         simplify_filename(file_name);
  1874.         if (mch_getperm(file_name) >= 0 && --count == 0)
  1875.         return file_name;
  1876.     }
  1877.     if (options & FNAME_MESS)
  1878.         EMSG2("Can't find file \"%s\" in path", NameBuff);
  1879.     }
  1880.  
  1881.     /* get here when file doesn't exist */
  1882.     vim_free(file_name);
  1883.     return NULL;
  1884. }
  1885.  
  1886. /*
  1887.  * find_file_in_wildcard_path(): expand path recursively while searching
  1888.  *                     files in path
  1889.  *
  1890.  * The syntax '**' means the whole subtree.
  1891.  * To avoid endless recursion, a counter restricts the depth to 100 levels.
  1892.  * In the following pseudo code '+/' will mean '*' followed by '/'
  1893.  *
  1894.  * in the case of 'set path=,/foo/bar/+/+/,'
  1895.  * the function call hierarchy will be
  1896.  *   find_file_in_wildcard_path("/", "foo/bar/+/+/", NameBuff, 0);
  1897.  *   find_file_in_wildcard_path("/foo/", "bar/+/+/", NameBuff, 0);
  1898.  *   find_file_in_wildcard_path("/foo/bar/", "+/+/", NameBuff, 0);
  1899.  * which in turn will call
  1900.  *   find_file_in_wildcard_path("/foo/bar/dir/", "+/", NameBuff, 1);
  1901.  * for each directory 'dir' in '/foo/bar/+'.  It's the next call,
  1902.  *   find_file_in_wildcard_path("/foo/bar/dir/dir2/", "", NameBuff, 2);
  1903.  * that will try to find the file 'NameBuff' in the given directory.
  1904.  *
  1905.  * pseudo code:
  1906.  *
  1907.  *  find_file_in_wildcard_path(path_so_far, wildcards, level)
  1908.  *  {
  1909.  *    if (level > 100)
  1910.  *    return NULL;
  1911.  *
  1912.  *    file_name = path_so_far + first_segment(wildcards);
  1913.  *    rest_of_wildcards = all_but_first_segment(wildcards);
  1914.  *
  1915.  *    result = expand(file_name);
  1916.  *
  1917.  *    if (!rest_of_wildcards) {
  1918.  *    foreach_path_in(result) {
  1919.  *      if (exists&readable(path + NameBuff))
  1920.  *        return path+NameBuff;
  1921.  *    }
  1922.  *    } else {
  1923.  *    foreach_path_in(result) {
  1924.  *      c = find_file_in_wildcard_path(path, rest_of_wildcards, level+1);
  1925.  *      if (c)
  1926.  *        return c;
  1927.  *    }
  1928.  *    }
  1929.  *    if (infinite_recursion(wildcards)) {
  1930.  *    foreach_path_in(result) {
  1931.  *      c = find_file_in_wildcard_path(path, wildcards, level+1);
  1932.  *      if (c)
  1933.  *        return c;
  1934.  *    }
  1935.  *    }
  1936.  *    return NULL;
  1937.  *  }
  1938.  */
  1939.     static char_u *
  1940. find_file_in_wildcard_path(path_so_far, wildcards, level, countptr)
  1941.     char_u  *path_so_far;
  1942.     char_u  *wildcards;
  1943.     int        level;
  1944.     long    *countptr;
  1945. {
  1946.     char_u  *file_name;
  1947.     int        len;
  1948.     char_u  *rest_of_wildcards;
  1949.     int        nFiles = 0;
  1950.     char_u  **ppFiles;
  1951.     int        i;
  1952.     char_u  *c;
  1953.  
  1954.     ui_breakcheck();
  1955.     if (level > 100 || got_int)
  1956.     return NULL;
  1957.  
  1958.     if ((file_name = alloc((int)MAXPATHL)) == NULL)
  1959.     return NULL;
  1960.  
  1961.     STRCPY(file_name, path_so_far);
  1962.     len = STRLEN(file_name);
  1963.     if (!vim_ispathsep(file_name[len-1]))
  1964.     {
  1965.     file_name[len++] = PATHSEP;
  1966.     file_name[len] = '\0';
  1967.     }
  1968.     rest_of_wildcards = wildcards;
  1969.     if (rest_of_wildcards)
  1970.     {
  1971.     if (STRNCMP(rest_of_wildcards, "**", 2) == 0)
  1972.         rest_of_wildcards++;
  1973.     while (*rest_of_wildcards && !vim_ispathsep(*rest_of_wildcards))
  1974.         file_name[len++] = *rest_of_wildcards++;
  1975.     /* file_name[len++] = *rest_of_wildcards++; */
  1976.     rest_of_wildcards++;
  1977.     file_name[len] = '\0';
  1978.     }
  1979.  
  1980.     ++expand_interactively;
  1981.     expand_wildcards(1, &file_name, &nFiles, &ppFiles, EW_FILE|EW_DIR);
  1982.     --expand_interactively;
  1983.  
  1984.     if (!*rest_of_wildcards)
  1985.     {
  1986.     for (i = 0; i < nFiles; ++i)
  1987.     {
  1988.         if (!mch_isdir(ppFiles[i]))
  1989.         continue;   /* not a directory */
  1990.         STRCPY(file_name, ppFiles[i]);
  1991.         if (!vim_ispathsep(file_name[STRLEN(file_name)-1]))
  1992.         STRCAT(file_name, PATHSEPSTR);
  1993.         STRCAT(file_name, NameBuff);
  1994.         if (mch_getperm(file_name) >= 0 && --*countptr == 0)
  1995.         {
  1996.         FreeWild(nFiles, ppFiles);
  1997.         return file_name;
  1998.         }
  1999.     }
  2000.     }
  2001.     else
  2002.     {
  2003.     for (i = 0; i < nFiles; ++i)
  2004.     {
  2005.         if (!mch_isdir(ppFiles[i]))
  2006.         continue;   /* not a directory */
  2007.         c = find_file_in_wildcard_path(ppFiles[i],
  2008.                     rest_of_wildcards, level+1, countptr);
  2009.         if (c)
  2010.         {
  2011.         FreeWild(nFiles, ppFiles);
  2012.         vim_free(file_name);
  2013.         return c;
  2014.         }
  2015.     }
  2016.     }
  2017.  
  2018.     if (STRNCMP(wildcards, "**", 2) == 0)
  2019.     {
  2020.     for (i = 0; i < nFiles; ++i)
  2021.     {
  2022.         if (!mch_isdir(ppFiles[i]))
  2023.         continue;   /* not a directory */
  2024.         c = find_file_in_wildcard_path(ppFiles[i],
  2025.                         wildcards, level+1, countptr);
  2026.         if (c)
  2027.         {
  2028.         FreeWild(nFiles, ppFiles);
  2029.         vim_free(file_name);
  2030.         return c;
  2031.         }
  2032.     }
  2033.     }
  2034.  
  2035.     FreeWild(nFiles, ppFiles);
  2036.  
  2037.     vim_free(file_name);
  2038.     return NULL;
  2039. }
  2040.  
  2041. /*
  2042.  * Check if the "://" of a URL is at the pointer, return URL_SLASH.
  2043.  * Also check for ":\\", which MS Internet Explorer accepts, return
  2044.  * URL_BACKSLASH.
  2045.  */
  2046.     static int
  2047. path_is_url(p)
  2048.     char_u  *p;
  2049. {
  2050.     if (STRNCMP(p, "://", (size_t)3) == 0)
  2051.     return URL_SLASH;
  2052.     else if (STRNCMP(p, ":\\\\", (size_t)3) == 0)
  2053.     return URL_BACKSLASH;
  2054.     return 0;
  2055. }
  2056. #endif /* FILE_IN_PATH */
  2057.  
  2058. /*
  2059.  * Return the minimal number of rows that is needed on the screen to display
  2060.  * the current number of windows.
  2061.  */
  2062.     int
  2063. min_rows()
  2064. {
  2065.     WIN        *wp;
  2066.     int        total;
  2067.  
  2068.     if (firstwin == NULL)    /* not initialized yet */
  2069.     return MIN_ROWS + 1;    /* one window plus command line */
  2070.  
  2071.     total = p_ch;    /* count the room for the status line */
  2072.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  2073.     total += MIN_ROWS + wp->w_status_height;
  2074.     return total;
  2075. }
  2076.  
  2077. /*
  2078.  * Return TRUE if there is only one window, not counting a help window, unless
  2079.  * it is the current window.
  2080.  */
  2081.     int
  2082. only_one_window()
  2083. {
  2084.     int        count = 0;
  2085.     WIN        *wp;
  2086.  
  2087.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  2088.     if (!wp->w_buffer->b_help || wp == curwin)
  2089.         ++count;
  2090.     return (count <= 1);
  2091. }
  2092.  
  2093. /*
  2094.  * Correct the cursor line number in other windows.  Used after changing the
  2095.  * current buffer, and before applying autocommands.
  2096.  * When "do_curwin" is TRUE, also check current window.
  2097.  */
  2098.     void
  2099. check_lnums(do_curwin)
  2100.     int        do_curwin;
  2101. {
  2102.     WIN        *wp;
  2103.  
  2104.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  2105.     if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf)
  2106.     {
  2107.         if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count)
  2108.         wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
  2109.         if (wp->w_topline > curbuf->b_ml.ml_line_count)
  2110.         wp->w_topline = curbuf->b_ml.ml_line_count;
  2111.     }
  2112. }
  2113.